#include "encoder.h"
#include "string.h"
#include "video_base.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>

H264Encoder::H264Encoder(/* args */) : IVidEncoder(),
                                       _enc_callbcak(NULL),
                                       _enc_callbcak_ctx(NULL),
                                       _isStart(false)
{
    memset( &_coder_config, 0, sizeof(CoderConfig) );
    memset( &_venc_base_config, 0, sizeof(VencBaseConfig) );
    memset( &_venc_h264_param, 0, sizeof(VencH264Param) );
    memset( &_venc_h264_buf_param, 0, sizeof(VencAllocateBufferParam) );
}

H264Encoder::~H264Encoder()
{
}

int32_t H264Encoder::Start(CoderConfig config)
{

    int32_t ret = 0;
    if( _isStart == true ){
        Stop();
    }

    _venc_base_config.memops = MemAdapterGetOpsS();

    CdcMemOpen( _venc_base_config.memops );
    _venc_base_config.nInputWidth= config.width;
    _venc_base_config.nInputHeight = config.height;
    _venc_base_config.nStride = config.width;
    _venc_base_config.nDstWidth = config.width;
    _venc_base_config.nDstHeight = config.height;
    _venc_base_config.eInputFormat = VENC_PIXEL_YVU420SP;

    _venc_handle = VideoEncCreate( VENC_CODEC_H264 );
    _venc_h264_param.bEntropyCodingCABAC = 1;
    _venc_h264_param.nBitrate = config.bitrate;
    _venc_h264_param.nCodingMode = VENC_FRAME_CODING;
    _venc_h264_param.nFramerate = config.fps;
    _venc_h264_param.nLongRefPoc = 0;
    _venc_h264_param.nMaxKeyInterval = config.bitrate;
    _venc_h264_param.sGopParam.bUseGopCtrlEn = 0;
    _venc_h264_param.sProfileLevel.nProfile = VENC_H264ProfileHigh;
    _venc_h264_param.sProfileLevel.nLevel = VENC_H264Level51;
    _venc_h264_param.sQPRange.nMinqp = 10;
    _venc_h264_param.sQPRange.nMaxqp = 50;

    VideoEncSetParameter(_venc_handle, VENC_IndexParamH264Param, &_venc_h264_param);

    VideoEncInit( _venc_handle, &_venc_base_config );

    _venc_h264_buf_param.nBufferNum = 1;
    _venc_h264_buf_param.nSizeY = config.width*config.height;
    _venc_h264_buf_param.nSizeC = config.width*config.height/2;

    AllocInputBuffer(_venc_handle, &_venc_h264_buf_param);

    _isStart = true;
    return 0;
}

int32_t H264Encoder::Stop()
{
    if( _isStart == false ){
        return -1;
        printf( "no start, please start frist.....\r\n" );
    }

    ReleaseAllocInputBuffer(_venc_handle);

    if( _venc_handle ){
        VideoEncDestroy(_venc_handle);
        _venc_handle = NULL;
    }

    if(_venc_base_config.memops)  //这里与sipeed提供的渲染接口冲突
    {
        CdcMemClose(_venc_base_config.memops);
        _venc_base_config.memops = NULL;
    }

    _isStart = false;
    return 0;
}

int32_t H264Encoder::Deliver(struct MemoryFrame *frame)
{
    int32_t ret = 0;
    VencInputBuffer inputBuffer;
    VencOutputBuffer outputBuffer;
    int32_t y_size = frame->w*frame->h;
    memset( &outputBuffer, 0, sizeof(VencOutputBuffer) );
    memset( &inputBuffer, 0, sizeof(VencInputBuffer) );
    GetOneAllocInputBuffer(_venc_handle, &inputBuffer);
    memcpy( inputBuffer.pAddrVirY, frame->addr, y_size );
    memcpy( inputBuffer.pAddrVirC, frame->addr+y_size, y_size/2 );
    inputBuffer.bEnableCorp = 0;
    FlushCacheAllocInputBuffer(_venc_handle, &inputBuffer);
    inputBuffer.nPts = frame->timestamp;
    AddOneInputBuffer(_venc_handle, &inputBuffer);
    VideoEncodeOneFrame(_venc_handle);
    AlreadyUsedInputBuffer(_venc_handle,&inputBuffer);
    ReturnOneAllocInputBuffer(_venc_handle, &inputBuffer);

    ret = GetOneBitstreamFrame(_venc_handle, &outputBuffer);
    if( ret == 0 ){
        VidEncOutputBuf venc_buffer;
        venc_buffer.key_flag = outputBuffer.nFlag;

        if( outputBuffer.nFlag == 1 ){ //I Frame
            VencHeaderData header_info;
            VideoEncGetParameter( _venc_handle, VENC_IndexParamH264SPSPPS, &header_info );
            venc_buffer.spspps_data = header_info.pBuffer;
            venc_buffer.spspps_data_size = header_info.nLength;
        }else{
            venc_buffer.spspps_data = NULL;
            venc_buffer.spspps_data_size = NULL;
        }

        venc_buffer.data = outputBuffer.pData0;
        venc_buffer.data_size = outputBuffer.nSize0;

        _enc_callbcak( &venc_buffer, _enc_callbcak_ctx );
    }
    FreeOneBitStreamFrame(_venc_handle, &outputBuffer);
    return ret;
}

int32_t H264Encoder::Regcb(VidEncoderCallback _callbcak, void *ctx)
{

    if( _callbcak != NULL ){
        _enc_callbcak = _callbcak;
        _enc_callbcak_ctx = ctx;
    }

    return 0;
}


int32_t aw_vid_enc_init( CoderConfig codec_config, /*out*/AW_VID_ENCODER_HANDLE *handle ){
    vid_enc_ins *ins = new vid_enc_ins();
    ins->codec_cfg.width = codec_config.width;
    ins->codec_cfg.height = codec_config.height;
    ins->codec_cfg.fps = codec_config.fps;
    ins->codec_cfg.bitrate = codec_config.bitrate;
    ins->IVidEnc = new H264Encoder();

    if( ins->IVidEnc == NULL ){
        return -1;
        printf( "get_instance failed\r\n" );
    }
    *handle = (AW_VID_ENCODER_HANDLE)ins;
    return 0;


}
int32_t aw_vid_enc_deinit( /*in*/AW_VID_ENCODER_HANDLE handle ){
    int32_t ret = 0;
    vid_enc_ins *ins = (vid_enc_ins*)handle;
    if( ins == NULL || ins->IVidEnc == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    delete ins->IVidEnc;
    delete ins;
    return ret;
}

int32_t aw_vid_enc_start( /*in*/AW_VID_ENCODER_HANDLE handle ){
    int32_t ret = 0;
    vid_enc_ins *ins = (vid_enc_ins*)handle;
    if( ins == NULL || ins->IVidEnc == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    ret = ins->IVidEnc->Start( ins->codec_cfg );
    if( ret < 0 ){
        printf( "aw_vid_enc_start failed..............\r\n" );
        return -1;
    }
    return ret;
}

int32_t aw_vid_enc_stop( /*in*/AW_VID_ENCODER_HANDLE handle ){
    int32_t ret = 0;
    vid_enc_ins *ins = (vid_enc_ins*)handle;
    if( ins == NULL || ins->IVidEnc == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    ret = ins->IVidEnc->Stop();
    if( ret < 0 ){
        printf( "aw_vid_enc_stop failed\r\n" );
        return -1;
    }
    return ret;


}

int32_t aw_vid_enc_regcb( /*in*/AW_VID_ENCODER_HANDLE handle, VidEncoderCallback enc_cb, void *ctx ){
    int32_t ret = 0;
    vid_enc_ins *ins = (vid_enc_ins*)handle;
    if( ins == NULL || ins->IVidEnc == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    ret = ins->IVidEnc->Regcb(enc_cb, ctx);
    if( ret < 0 ){
        printf( "aw_vid_enc_regcb failed\r\n" );
        return -1;
    }
    return ret;
}

int32_t aw_vid_enc_deliver( /*in*/AW_VID_ENCODER_HANDLE handle, struct MemoryFrame *frame ){

    int32_t ret = 0;
    vid_enc_ins *ins = (vid_enc_ins*)handle;
    if( ins == NULL || ins->IVidEnc == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    ret = ins->IVidEnc->Deliver(frame);
    if( ret < 0 ){
        printf( "aw_vid_encoder_deliver failed\r\n" );
        return -1;
    }
    return ret;

}
